#include <xp/net/tcp.h>
#include <xp/net/inet.h>
#include <xp/bas/stdio.h>
#include <xp/bas/stdlib.h>
#include <xp/bas/signal.h>
#include <xp/bas/string.h>
#include <xp/bas/mstring.h>
#include <xp/bas/locale.h>
#include <xp/bas/endian.h>

#ifdef __linux
#include <mcheck.h>
#endif

static xp_bool_t stop_requested = xp_false;

void handle_sigint (int sig)
{
	if (sig == SIGINT) stop_requested = xp_true;
}

int xp_main (int argc, xp_char_t* argv[])
{
	xp_tcp_t tcp;

	xp_byte_t* ip;
	xp_size_t iplen;

	xp_uint16_t port;
	int mode;

#ifdef __linux
	mtrace ();
#endif
	if (argc != 3) {
		xp_printf (XP_TEXT("usage: %s port mode\n"), argv[0]);
		xp_printf (XP_TEXT(" where mode := 4 | 6\n"));
		return -1;
	}

	if (xp_setlocale () == -1) {
		xp_printf (XP_TEXT("cannot set locale\n"));
		return -1;
	}

	port = (xp_uint16_t)xp_atouint(argv[1]);
	mode = xp_atoi(argv[2]);

	if (mode != 4 && mode != 6) {
		xp_printf (XP_TEXT("error: mode should be either 4 or 6\n"));
		return -1;
	}

	xp_printf (XP_TEXT("port: %d mode: %d\n"), (int)port, mode);
	xp_signal (SIGINT, handle_sigint);

	xp_sckstartup ();

	if (xp_tcp_open(&tcp, XP_TCP_TEXT) == XP_NULL) {
		xp_printf (XP_TEXT("cannot open tcp\n"));
		return -1;
	}

	if (mode == 4) {
		ip = xp_inet4_any;
		iplen = xp_sizeof(xp_inet4_any);
		//ip = xp_inet4_loopback;
		//iplen = xp_sizeof(xp_inet4_loopback);
	}
	else {
		ip = xp_inet6_any;
		iplen = xp_sizeof(xp_inet6_any);
		//ip = xp_inet6_loopback;
		//iplen = xp_sizeof(xp_inet6_loopback);
	}

	if (xp_tcp_bind (&tcp, xp_hton16(port), ip, iplen, 0) == -1) {

	if (xp_tcp_bind (&tcp, xp_hton16(port), ip, iplen, 0) == -1) {
		xp_printf (XP_TEXT("cannot bind\n"));
		xp_tcp_close (&tcp);
		return -1;
	}

	}

	if (xp_tcp_lstn (&tcp, 1) == -1) {
		xp_printf (XP_TEXT("cannot listen\n"));
		xp_tcp_close (&tcp);
		return -1;
	}

	xp_tcp_settmout (&tcp, XP_TCP_ACPT, 1000);

/*
{
	char buf[1000];
	sprintf (buf, "ls -l /proc/%d/fd", getpid());
	system (buf);
}
*/

	while (!stop_requested) {
		xp_tcp_t* client = xp_tcp_acpt(&tcp, XP_NULL);
		if (client == XP_NULL) {
			if (xp_tcp_geterrnum(&tcp) == XP_TCP_ETMOUT) {
				if (stop_requested) break;
				continue;
			}
			else {
				xp_printf (
					XP_TEXT("cannot accept client - %s\n"), 
					xp_tcp_geterrstr(&tcp));
				break;
			}
		}

		if (stop_requested) {
			xp_tcp_close (client);
			break;
		}

		xp_printf (XP_TEXT("accepted connection\n"));
		xp_tcp_settmout (client, XP_TCP_RECV, 10000);

		while (1) {
			xp_char_t buf[1000];
			xp_ssize_t n;
			n = xp_tcp_recv (client, buf, xp_countof(buf) - 1);
			if (n == -1) {
				xp_printf (
					XP_TEXT("cannot receive data - %s\n"), 
					xp_tcp_geterrstr(client));
				break;
			}
			if (n == 0) {
				xp_printf (XP_TEXT("connection closed\n"));
				break;
			}
			buf[n] = XP_CHAR('\0');

			if (xp_strcasecmpn (buf, XP_TEXT("quit"), 4) == 0) break;

			xp_printf (XP_TEXT("[%s]\n"), buf);
			xp_tcp_send (client, buf, xp_strlen(buf));
		}

		xp_tcp_close (client);
	}

	xp_tcp_close (&tcp);
	xp_printf (XP_TEXT("exiting...\n"));
#ifdef __linux
	//muntrace ();
#endif
/*
{
	char buf[1000];
	sprintf (buf, "ls -l /proc/%d/fd", getpid());
	system (buf);
}
*/
	return 0;
}

